package compoundDB.database;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

import javax.sql.rowset.CachedRowSet;

import com.sun.rowset.CachedRowSetImpl;

import compoundDB.core.Compound;
import compoundDB.database.container.ColumnField;

/**
 * This class is an implementation of the QueryReader interface. It loads the
 * entire ResultSet into memory, and is then able to quickly access the
 * ResultSet randomly.
 * 
 * @author Kohl Bromwich
 * 
 */
public class CachedQueryReader implements QueryReader
{
	/**
	 * The parent used to get the names of compounds from the database.
	 */
	private DatabaseAccessor m_databaseAccessor;
	
	/**
	 * Where the ResultSet data is stored.
	 */
	private CachedRowSet m_results;
	
	/**
	 * The row count of the results, for quick reference.
	 */
	private int m_rowCount;
	
	/**
	 * The columns in the database upon result creation.
	 */
	List<ColumnField> columns;
	
	/**
	 * Creates an instance of a cached query reader, loading the supplied
	 * ResultSet into memory upon construction.
	 * 
	 * @param databaseAccessor
	 *            The parent used to get the names of compounds from the
	 *            database.
	 * 
	 * @param results
	 *            The ResultSet to read compounds out of.
	 * 
	 * @throws SQLException
	 *             if an error occurs communicating with the database.
	 */
	public CachedQueryReader(DatabaseAccessor databaseAccessor,
			ResultSet results) throws SQLException
	{
		m_databaseAccessor = databaseAccessor;
		m_results = new CachedRowSetImpl();
		m_results.populate(results);
		results.close();
		
		m_results.last();
		m_rowCount = m_results.getRow();
		
		columns = m_databaseAccessor.getCompoundTableColumns();
	}
	
	@Override
	public void close() throws SQLException
	{
		if (m_results != null)
		{
			m_results.close();
		}
		m_rowCount = -1;
	}
	
	/**
	 * Destructor that is called before garbage collection. Will close the
	 * result set if it is still open.
	 */
	protected void finalize()
	{
		try
		{
			close();
		} catch (SQLException e)
		{
			e.printStackTrace();
		}
	}
	
	@Override
	public Compound getCompound() throws SQLException
	{
		
		if (m_results.isAfterLast())
		{
			return null;
		}
		
		int guid = m_results.getInt("guid");
		int ownerid = m_results.getInt("ownerid");
		
		Compound comp = new Compound(guid, ownerid);
		
		for (ColumnField col : columns)
		{
			switch (col.getDataType())
			{
			case STRING:
				comp.setProperty(col.getName(), m_results.getString(col
						.getColumnNumber()));
				break;
			case LONG:
				comp.setProperty(col.getName(), m_results.getLong(col
						.getColumnNumber()));
				break;
			case INTEGER:
				comp.setProperty(col.getName(), m_results.getInt(col
						.getColumnNumber()));
				break;
			case FLOAT:
				comp.setProperty(col.getName(), m_results.getFloat(col
						.getColumnNumber()));
				break;
			case DOUBLE:
				comp.setProperty(col.getName(), m_results.getDouble(col
						.getColumnNumber()));
				break;
			case BYTES:
				comp.setProperty(col.getName(), m_results.getBytes(col
						.getColumnNumber()));
				break;
			default:
				// Should not reach here
				break;
			}
		}

		comp.setNames(m_databaseAccessor.getNames(guid));
		
		m_results.next();
		
		return comp;
	}
	
	@Override
	public int getCompoundCount() throws SQLException
	{
		if (m_rowCount < 0)
		{
			throw new SQLException("Reader is closed.");
		}
		return m_rowCount;
	}
	
	@Override
	public List<Compound> getCompounds(int startCompound, int numOfCompounds)
			throws SQLException
	{
		if (startCompound < 1)
		{
			throw new IllegalArgumentException(
					"startCompound must be greater than 0");
		}
		if (m_rowCount < 0)
		{
			throw new SQLException("Reader is closed");
		}
		
		m_results.absolute(startCompound);
		
		List<Compound> comps = new ArrayList<Compound>();
		
		for (int count = 0; count < numOfCompounds; count++)
		{
			Compound comp = getCompound();
			if (comp == null)
			{
				break;
			}
			comps.add(comp);
		}
		
		return comps;
	}
	
	@Override
	public void seek(int posCompound) throws SQLException
	{
		m_results.absolute(posCompound);
	}
	
}
